package com.desaysv.wandroid.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.view.Surface;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author linzi
 * @version 1.0
 * @date 2023-3-24
 * @since 2023-3-24
 */
public final class ProjectManager {
    /**
     * 单例对象
     */
    @SuppressLint("StaticFieldLeak")
    private static volatile ProjectManager mInstance;

    /**
     * 私有构造函数
     */
    private ProjectManager() {
    }

    /**
     * 获取单例
     *
     * @return 单例对象
     */
    public static ProjectManager getInstance() {
        if (mInstance == null) {
            synchronized (ProjectManager.class) {
                if (mInstance == null) {
                    mInstance = new ProjectManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 投屏媒体对象
     */
    private MediaProjection mMediaProject;
    /**
     * 线程池
     */
    private final ExecutorService mPool = Executors.newFixedThreadPool(2);
    /**
     * 上下文对象
     */
    private Context mContext;

    /**
     * 编码器
     */
    private MediaCodec mMediaEncodec;
    /**
     * 解码器
     */
    private MediaCodec mMediaDecodec;
    /**
     * 是否开始录制
     */
    private boolean isStartRecording = false;
    /**
     * 是否开始解码
     */
    private boolean isStartDecoder = false;
    /**
     * 超时时间
     */
    private final int timeOut = 1000;
    /**
     * I帧
     */
    private static final int TYPE_FRAME_INTERVAL = 19;
    /**
     * vps帧
     */
    private static final int TYPE_FRAME_VPS = 32;
    /**
     * 记录vps pps sps
     */
    private byte[] vpsPpsSps;

    /**
     * 回调对象
     */
    private RecordCallBack mCallback;

    /**
     * 初始化
     *
     * @param context    上下文对象
     * @param resultCode 来自于授权之后的结果
     * @param data       来自于授权之后的结果
     * @param callBack   回调
     *                   init前置申请，resultCode和data来自于授权之后的结果
     *                   Intent captureIntent = ProjectionService.getInstance().getProjectionManager().createScreenCaptureIntent();
     *                   startActivityForResult(captureIntent, REQUEST_CODE);
     */
    public void initRecord(Context context, int resultCode, Intent data, RecordCallBack callBack) {
        if (mContext != null) {
            return;
        }
        this.mContext = context;
        this.mCallback = callBack;

        MediaProjectionManager projectionManager = (MediaProjectionManager) mContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        mMediaProject = projectionManager.getMediaProjection(resultCode, data);
    }

    /**
     * 回调接口
     */
    public interface RecordCallBack {
        /**
         * @param data 帧数据
         */
        void recordingData(byte[] data);

        /**
         * 错误信息
         *
         * @param e 异常
         */
        void error(Throwable e);
    }

    /**
     * 开始录屏
     *
     * @param width          视频宽度
     * @param height         视频高度
     * @param rate           视频帧率
     * @param iFrameInterval 设置I帧帧率
     */
    public void startRecord(int width, int height, int rate, int iFrameInterval) {
        if (isStartRecording) {
            mCallback.error(new Throwable("already started"));
        } else {
            MediaFormat mediaFormat =
                    MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height);
            mediaFormat.setInteger(
                    MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            // 比特率（比特/秒）
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height);
            // 帧率
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, rate);
            // I帧的频率
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
            if (mMediaEncodec == null) {
                try {
                    mMediaEncodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
                } catch (IOException e) {
                    e.printStackTrace();
                    mCallback.error(e);
                    return;
                }
            }
            mMediaEncodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

            Surface surface = mMediaEncodec.createInputSurface();
            mMediaProject.createVirtualDisplay(
                    "screen",
                    width,
                    height,
                    1,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                    surface,
                    null,
                    null);
            isStartRecording = true;

            mPool.execute(() -> {
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                while (isStartRecording) {
                    int outPutBufferId = mMediaEncodec.dequeueOutputBuffer(bufferInfo, timeOut);
                    if (outPutBufferId >= 0) {
                        ByteBuffer byteBuffer = mMediaEncodec.getOutputBuffer(outPutBufferId);
                        encodeData(byteBuffer, bufferInfo);
                        mMediaEncodec.releaseOutputBuffer(outPutBufferId, false);
                    }

                }
            });
        }
    }

    /**
     * 编码视频数据【H.265】
     *
     * @param byteBuffer 数据帧
     * @param bufferInfo 编码器缓冲数据
     */
    private void encodeData(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) {
        int offSet = 4;
        if (byteBuffer.get(2) == 0x01) {
            offSet = 3;
        }
        int type = (byteBuffer.get(offSet) & 0x7E) >> 1;
        if (type == TYPE_FRAME_VPS) {
            vpsPpsSps = new byte[bufferInfo.size];
            byteBuffer.get(vpsPpsSps);
        } else if (type == TYPE_FRAME_INTERVAL) {
            final byte[] bytes = new byte[bufferInfo.size];
            byteBuffer.get(bytes);
            byte[] newBytes = new byte[vpsPpsSps.length + bytes.length];
            System.arraycopy(vpsPpsSps, 0, newBytes, 0, vpsPpsSps.length);
            System.arraycopy(bytes, 0, newBytes, vpsPpsSps.length, bytes.length);
            mCallback.recordingData(newBytes);
        } else {
            byte[] bytes = new byte[bufferInfo.size];
            byteBuffer.get(bytes);
            mCallback.recordingData(bytes);
        }
    }

    /**
     * 停止录屏
     */
    public void stopRecording() {
        if (!isStartRecording) {
            return;
        }
        if (mMediaProject != null) {
            mMediaProject.stop();
        }
        if (mMediaEncodec != null) {
            mMediaEncodec.release();
            mMediaEncodec = null;
        }
        vpsPpsSps = null;
        isStartRecording = false;
    }

    /**
     * 开始解码
     * @param surface 解码显示的surface
     * @param width 视频宽度
     * @param height 视频高度
     * @param rate 视频帧率
     * @param iFrameInterval I帧帧率
     */
    public void startDecode(Surface surface, int width, int height, int rate, int iFrameInterval) {
        if (isStartDecoder) {
            throw new RuntimeException("already start decoder");
        }
        try {
            // 配置MediaCodec
            if (mMediaDecodec == null) {
                mMediaDecodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC);
            }

            MediaFormat mediaFormat =
                    MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height);
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height);
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, rate);
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
            mMediaDecodec.configure(mediaFormat, surface, null, 0);
            mMediaDecodec.start();
            isStartDecoder = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解码数据
     * @param data 视频帧数据，接受到数据之后需要在子线程执行
     */
    public void decodeData(byte[] data) {
        if (!isStartDecoder) {
            throw new RuntimeException("decoder not start");
        }
        int index = mMediaDecodec.dequeueInputBuffer(timeOut);
        if (index >= 0) {
            ByteBuffer inputBuffer = mMediaDecodec.getInputBuffer(index);
            inputBuffer.clear();
            inputBuffer.put(data, 0, data.length);
            mMediaDecodec.queueInputBuffer(index, 0, data.length, System.currentTimeMillis(), 0);
        }
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mMediaDecodec.dequeueOutputBuffer(bufferInfo, timeOut);
        while (outputBufferIndex > 0) {
            mMediaDecodec.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = mMediaDecodec.dequeueOutputBuffer(bufferInfo, 0);
        }
    }

    /**
     * 停止解码
     */
    public void stopDecode() {
        if (mMediaEncodec != null) {
            mMediaDecodec.release();
            mMediaDecodec = null;
            isStartDecoder = false;
        }
    }

}
